﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;

using PipeClient.Interface;
using PipeClient.Model;

using RpcHelper;

namespace PipeClient.Manage
{
        internal class ClientManage
        {
                private static readonly ConcurrentDictionary<string, ServerInfo> _ServerList = new ConcurrentDictionary<string, ServerInfo> ();


                private static readonly Timer _Heartbeat = new Timer (new TimerCallback (_SendHeartbeat), null, Config.PipeConfig.HeartbeatTime, Config.PipeConfig.HeartbeatTime);

                private static void _SendHeartbeat (object res)
                {
                        string[] keys = _ServerList.Keys.ToArray ();
                        Array.ForEach (keys, a =>
                         {
                                 if (_ServerList.TryGetValue (a, out ServerInfo server))
                                 {
                                         server.SendHeartbeat ();
                                 }
                         });
                }
                public static void CloseClient (string serverId)
                {
                        if (_ServerList.TryRemove (serverId, out ServerInfo server))
                        {
                                server.CloseClient ();
                        }
                }
                public static void CloseClient ()
                {
                        string[] keys = _ServerList.Keys.ToArray ();
                        keys.ForEach (a =>
                         {
                                 if (_ServerList.TryRemove (a, out ServerInfo server))
                                 {
                                         server.CloseClient ();
                                 }
                         });
                }
                public static string GetServerId (string ip, string pipeName)
                {
                        string id = string.Format ("{0}:{1}", ip, pipeName);
                        if (_ServerList.TryGetValue (id, out ServerInfo server))
                        {
                                return server.Id;
                        }
                        server = _ServerList.GetOrAdd (id, new ServerInfo (ip, pipeName));
                        server.InitServer ();
                        return server.Id;
                }
                public static bool GetServer (string serverId, out ServerInfo server, out string error)
                {
                        if (_ServerList.TryGetValue (serverId, out server))
                        {
                                error = null;
                                return true;
                        }
                        error = "pipe.server.not.find";
                        return false;
                }

                internal static void ClientClose (IClient client)
                {
                        if (_ServerList.TryGetValue (client.ServerId, out ServerInfo server))
                        {
                                server.RemoveClient (client);
                        }
                }
                public static bool CheckClientIsUsable (string serverId)
                {
                        return _ServerList.TryGetValue (serverId, out ServerInfo server) && server.CheckIsUsable ();
                }

                public static bool CheckIsUsable (string serverId, out string error)
                {
                        if (!GetServer (serverId, out ServerInfo server, out error))
                        {
                                return false;
                        }
                        else if (server.CheckIsUsable ())
                        {
                                return true;
                        }
                        else if (!PageManage.Send (Page.GetPingPage (serverId), out string res, out error))
                        {
                                return false;
                        }
                        else if (res != "ok")
                        {
                                error = "pipe.reply.no.ok";
                                return false;
                        }
                        return true;
                }
                public static bool Ping (string serverId, out TimeSpan ping)
                {
                        DateTime send = DateTime.Now;
                        if (!PageManage.Send (Page.GetPingPage (serverId), out string res, out _))
                        {
                                ping = TimeSpan.Zero;
                                return false;
                        }
                        else if (res != "ok")
                        {
                                ping = TimeSpan.Zero;
                                return false;
                        }
                        ping = DateTime.Now - send;
                        return true;
                }
                internal static int GetClientConNum (string serverId)
                {
                        if (_ServerList.TryGetValue (serverId, out ServerInfo server))
                        {
                                return server.ClientNum;
                        }
                        return 0;
                }
        }
}
